# client
# udp_http_client
vv='V11.2.20230507'
#使用前记得切换测试模式正式模式
import socket
import time
import _thread
from machine import reset
from machine import Pin,Signal
try:    #当esp32c3未安装urequests时,先进行自动安装该库
    import urequests
except:
    import upip
    upip.install('micropython-urequests')
    import urequests

print('udp_http_client',vv)
######################################################################
'''
#########正式环境#########
#初始化GPIO
led2=Signal(Pin(2, Pin.OPEN_DRAIN), invert=True) #电动车插座  #低电平触发继电器所以用开漏模式增强
led3=Signal(Pin(3, Pin.OUT), invert=True)  #小灯
led10=Signal(Pin(10, Pin.OUT), invert=True)  #大灯
led2.off()
led3.off()
led10.off()

#固定两个自带的led的电平
led12=Signal(Pin(12, Pin.OUT), invert=False)
led13=Signal(Pin(13, Pin.OUT), invert=False)
led12.off()
led13.off()

import network
mac_data=network.WLAN().config('mac')

import ubinascii
mac_str = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
device_name=mac_str

server_addr=('42.192.57.31',8266)
#########正式环境#########
'''
######################################################################


#########debug环境#########
#初始化GPIO
led2=Signal(Pin(2, Pin.OUT), invert=False) #电动车插座  #低电平触发继电器所以用开漏模式增强
led3=Signal(Pin(12, Pin.OUT), invert=False)  #小灯
led10=Signal(Pin(13, Pin.OUT), invert=False)  #大灯

led2.off()
led3.off()
led10.off()

import network
mac_data=network.WLAN().config('mac')

import ubinascii
mac_str = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
device_name=mac_str

#server_addr=('192.168.8.7',8299)
server_addr=('42.192.57.31',8299)

#########debug环境#########

######################################################################

#固件更新相关

def update_firmware():  #   强制更新(直接更新)
    with open('main.py','w') as f:
        cloud_firmware=urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_test/update_firmware.py').text
        f.write(cloud_firmware) #更新固件
    with open('firmware_version.txt','w') as f: #更新固件版本号
        f.write(str(eval(urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_test/update_version.py').text)))
        print('固件更新成功')
        print('reboot')
    
    time.sleep(2)   #确保文件正常保存
    reset() #重启

def get_version():  #版本获取,如失败则强制更新
    try:
        with open('firmware_version.txt','r') as f:
            local_version=eval(f.read()).get('version')
            if local_version:
                udp.sendto('#v#{}#{}'.format(device_name,local_version).encode(),addr)
        return local_version
    except:
        print('获取本地版本失败,请检查版本文件,程序尝试更新至最新')
        return 999

def auto_update_firmware(): #智能更新,版本获取,先判断再更新,如失败则强制更新
    try:
        with open('firmware_version.txt','r') as f:
            local_version=eval(f.read()).get('version')
    except: 
        update_firmware()
    cloud_version=eval(urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_test/update_version.py').text).get('version')
    if cloud_version > local_version:
        update_firmware()

######################################################################

# udp主服务
udp=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建udp socket对象

#GPIO状态心跳包(一包两用,既传心跳也传状态)
def heart(udp):
    while True:
        GPIO_STATE={'gpio1':led3.value(),'gpio2':led10.value(),'gpio3':led2.value()}
        udp.sendto('#c#{}#{}'.format(device_name,GPIO_STATE).encode(),server_addr)
        time.sleep(120) #双向心跳包(两个心跳包必须要有一个间隔是在120秒内的,因为UDP保活的机制)

#_thread.start_new_thread(target=heart,args=(udp,)) #注意,这样在micropython是错误的
_thread.start_new_thread(heart,(udp,)) #正确

#开启循环监听
while True:
    try:
        #udp.sendto('#c#{}'.format(device_name).encode(),server_addr)    #客户端注册
        msg,addr=udp.recvfrom(1024)
        print('addr:',addr)
        msg=msg.decode()  #这里存在潜在的错误，需要异常捕获
        print('data:',msg)

        #老式版本GPIO接口
        if msg=='led1on':   #开led1
            led3.on()
        if msg=='led1off':  #关led1
            led3.off()
        if msg=='led2on':   #开led2
            led10.on()
        if msg=='led2off':  #关led2
            led10.off()
        if msg=='ledoff':   #开led1/2
            led3.off()
            led10.off()
        if msg=='ledon':    #关led1/2
            led3.on()
            led10.on()                    
        if msg=='caron':    #开插座
            led2.on()
        if msg=='caroff':   #关插座
            led2.off()
        if msg=='allon':    #全开
            led3.on()
            led10.on()
            led2.on()
        if msg=='alloff':   #全关
            led3.off()
            led10.off()
            led2.off()
        if msg=='get_state':    #获取GPIO状态
            GPIO_STATE={'gpio1':led3.value(),'gpio2':led10.value(),'gpio3':led2.value()}
            udp.sendto('#c#{}#{}'.format(device_name,GPIO_STATE).encode(),addr)
        
        #新版本GPIO接口
        if msg=='gpio1':    #gpio1单控反转
            led1_temp_state=led3.value()
            if led1_temp_state==0:
                led3.on()
            else:
                led3.off()

        if msg=='gpio2':    #gpio2单控反转
            led1_temp_state=led10.value()
            if led1_temp_state==0:
                led10.on()
            else:
                led10.off()

        if msg=='gpio3':    #gpio3单控反转
            led1_temp_state=led2.value()
            if led1_temp_state==0:
                led2.on()
            else:
                led2.off()

        if msg=='gpio3on':  #gpio3开启
            led2.on()

        if msg=='gpio3off': #gpio3关闭
            led2.off()

        # 推送固件升级接口
        if msg=='get_version':    #获取固件版本
            get_version()     
        #if msg=='update_firmware':    #检查固件并自动更新
        #    auto_update_firmware()
        if msg=='update_firmware':    #检查固件并自动更新
            update_firmware()    #强制固件更新_测试和环境使用

    except Exception as e:
        print(e)
        print('重新监听...')

######################################################################
